library(data.table)     # Run once per session
library(ggplot2)        # Run once per session
library(igraph)         # Run once per session

dt.trade <- fread("../../data/cleaned_trade_data.csv")
dt.trade
# Set the parameters
dt.trade.country <- dt.trade[reporter_name == "Afghanistan"]
dt.trade.country.year <- dt.trade.country[year == 2017]

# Set the vertices
dt.all.reporters <- dt.trade.country.year[, list(name=unique(reporter_name), type=FALSE)]
dt.all.partners <- dt.trade.country.year[, list(name=unique(partner_name), type=TRUE)]

dt.all.vertices <- rbind(dt.all.reporters, dt.all.partners)

# Create & plot the graph
g <- graph.data.frame(dt.trade.country.year[, list(reporter_name, partner_name)], directed=TRUE, vertices=dt.all.vertices)

plot(g, vertex.size = 5, vertex.label.cex = 0.6)


# Add weights to the graph & plot
g <- set_edge_attr(g, "weight", value= dt.trade.country.year$trade_value_usd)

plot(g, vertex.size = 5, vertex.label.cex = 0.6)

# 1. Make edge list
dt.trade.year <- dt.trade[year == 2021]
dt.trade.year
dt.trade.edgelist <- dt.trade.year[ , c('reporter_name', 'partner_name')]
dt.trade.edgelist


# 2. Convert edge list to an igraph network
# igraph wants our data in matrix format
m.trade <- as.matrix(dt.trade.edgelist) 
g.trade <- graph_from_edgelist(m.trade, directed=TRUE)


# 3. Set the weight of the edges (trade_value_usd) & add the year as an attribute
E(g.trade)$weight <- dt.trade.year$trade_value_usd
  # Sum the weights for duplicate edges (we would need this if we select multiple years)
  g.trade <- simplify(g.trade, remove.multiple = TRUE, edge.attr.comb=list(weight="sum", year="concat"))

# 4. Add attributes to the vertices (continent)
library(rjson)
json_data <- fromJSON(file='../../data/sample.json')
V(g.trade)$continent <-  unlist(json_data[V(g.trade)$name])

# 5. Plot igraph
plot(g.trade, vertex.size = 5, vertex.label.cex = 0.3, edge.arrow.size=0.4)

# 6. Community detection algorithms
# Run Walktrap algorithm
walktrap_communities <- walktrap.community(g.trade, weights = E(g.trade)$weight)
# Print the number of communities detected
cat("Number of communities detected by Walktrap algorithm: ", length(unique(walktrap_communities$membership)), "\n")
Number of communities detected by Walktrap algorithm:  3 
# Plot the resulting clusters
plot(walktrap_communities, g.trade, vertex.size = 5, vertex.label.cex = 0.3, edge.arrow.size=0.4, vertex.color = walktrap_communities$membership)


#Modularity score: One way to evaluate the quality of the clustering is to calculate the modularity score, which measures the extent to which the nodes within a community are more densely connected to each other than to nodes outside the community. 
modularity(walktrap_communities, g)
[1] 0.2609934
# get the size of each community
sizes(walktrap_communities)
Community sizes
  1   2   3 
153  73   1 
# Create a new graph object with community memberships as vertex attributes
g_comm <- set_vertex_attr(g.trade, "community", value = walktrap_communities$membership)
# Convert to igraph object
g_comm <- graph_from_data_frame(get.edgelist(g_comm), directed = TRUE)
# Compute edge betweenness for the entire graph
eb <- igraph::edge.betweenness(g)
# Find the top 10 edges with the highest edge betweenness
top10_eb <- order(eb, decreasing = TRUE)[1:10]
# Generate a vector of colors using the rainbow() function
color_eb <- rainbow(length(E(g)))
# Set the color of the top 10 edges with highest edge betweenness to red
color_eb[top10_eb] <- "red"
# Set the color of the remaining edges to transparent
color_eb[-top10_eb] <- "transparent"

# get the average path length for each community
apl_comm <- lapply(unique(walktrap_communities$membership), function(m) average.path.length(induced_subgraph(g.trade, V(g.trade)[walktrap_communities$membership == m])))
apl_all <- average.path.length(g.trade)
print(apl_all)
[1] 525549.7
print(apl_comm)
[[1]]
[1] 5301707

[[2]]
[1] 2245355

[[3]]
[1] NaN
plot(g.trade, vertex.size = 5, vertex.label.cex = 0.3, edge.arrow.size=0.4, vertex.color = walktrap_communities$membership, edge.color = color_eb)

# SHINY WITHOUT WEIGTH MIN AND WITHOUT MAP

library(data.table)
library(ggplot2)
library(igraph)
library(shiny)


# Load data from file comptab_2018-01-29 16_00_comma_separated.csv 
dt.trade <- fread("../../data/cleaned_trade_data.csv") 

# Define UI --------------------------------------------------------------------

ui <- fluidPage(
  
  # App title ----
  titlePanel("Shiny App for International Trade"),
  
  sidebarLayout(
    
    # Inputs: Select variables to plot
    sidebarPanel(
      
      # Set year range
      selectInput(inputId = "year",
                  label = "Select Year Range:",
                  choices = c('', levels(as.factor(dt.trade$year))),
                  selected = '2021',
                  multiple = FALSE)
    ),
    
    
    # Output: Show network
    mainPanel(
      plotOutput("cluster"),
      htmlOutput("text")
    )
  )
)


# Define server ----------------------------------------------------------------

server <- function(input, output, session) {
  
  output$cluster <- renderPlot({
    
    # 1. Make edge list
    dt.trade.year <- dt.trade[dt.trade$year == input$year, ]
    dt.trade.edgelist <- dt.trade.year[ , c('reporter_name', 'partner_name')]
    
    # 2. Convert edge list to an igraph network
    # igraph wants our data in matrix format
    m.trade <- as.matrix(dt.trade.edgelist) 
    g.trade <- graph_from_edgelist(m.trade, directed=TRUE)
    
    # 3. Set the weight of the edges (trade_value_usd) & add the year as an attribute
    E(g.trade)$weight <- dt.trade.year$trade_value_usd
    
    # 4. Community detection algorithms
    # Run Walktrap algorithm
    walktrap_communities <- walktrap.community(g.trade, weights = E(g.trade)$weight)
    
    # Create a new graph object with community memberships as vertex attributes
    g_comm <- set_vertex_attr(g.trade, "community", value = walktrap_communities$membership)
    # Convert to igraph object
    g_comm <- graph_from_data_frame(get.edgelist(g_comm), directed = TRUE)
    # Compute edge betweenness for the entire graph
    eb <- igraph::edge.betweenness(g_comm)
    # Find the top 10 edges with the highest edge betweenness
    top10_eb <- order(eb, decreasing = TRUE)[1:10]
    # Generate a vector of colors using the rainbow() function
    color_eb <- rainbow(length(E(g_comm)))
    # Set the color of the top 10 edges with highest edge betweenness to red
    color_eb[top10_eb] <- "red"
    # Set the color of the remaining edges to transparent
    color_eb[-top10_eb] <- "transparent"
    
    plot(walktrap_communities, g.trade, vertex.size = 5, vertex.label.cex = 0.3, edge.arrow.size=0.4, vertex.color = walktrap_communities$membership, edge.color = color_eb)
    
  })
  
  output$text <- renderUI({
    # 1. Make edge list
    dt.trade.year <- dt.trade[dt.trade$year == input$year, ]
    dt.trade.edgelist <- dt.trade.year[ , c('reporter_name', 'partner_name')]
    
    # 2. Convert edge list to an igraph network
    # igraph wants our data in matrix format
    m.trade <- as.matrix(dt.trade.edgelist) 
    g.trade <- graph_from_edgelist(m.trade, directed=TRUE)
    
    # 3. Set the weight of the edges (trade_value_usd) & add the year as an attribute
    E(g.trade)$weight <- dt.trade.year$trade_value_usd
    
    # 6. Community detection algorithms
    # Run Walktrap algorithm
    walktrap_communities <- walktrap.community(g.trade, weights = E(g.trade)$weight)
    
    # get the average path length for each community
    apl_comm <- lapply(unique(walktrap_communities$membership), function(m) average.path.length(induced_subgraph(g.trade, V(g.trade)[walktrap_communities$membership == m])))
    apl_all <- average.path.length(g.trade)
    
    HTML(paste("Number of communities detected by Walktrap algorithm: ", length(unique(walktrap_communities$membership)), "<br>",
             "Modularity score of Walktrap algorithm: ", modularity(walktrap_communities, g), "<br>",
             "Sizes of the communities: ", paste(unname(sizes(walktrap_communities)), collapse = ", "), "<br>",
             " ", "<br>",
             "Average path length of the total trade network: ", apl_all, "<br>",
             "Average path length of the communities: ", paste(unname(apl_comm), collapse = ", "), "<br>"))
  })
}

# Create a Shiny app object ----------------------------------------------------

shinyApp(ui = ui, server = server)

Listening on http://127.0.0.1:4934
NA
# SHINY WITH WEIGHT MIN AND MAP
library(data.table)
library(ggplot2)
library(scales)
library(igraph)
library(shiny)
library(shinyWidgets)
library(DT)             
library(shiny)         
library(ggmap)
library(leaflet)
library(bslib)
library(sf)
library(geosphere)
library(RColorBrewer)
library(sp)
library(dplyr)
library(remotes)
library(magrittr)
library(devtools)

dt.trade <- fread("../../data/cleaned_trade_data.csv")

# Define UI --------------------------------------------------------------------

ui <- fluidPage(
  
  # App title ----
  titlePanel("Shiny App for International Trade"),
  
  sidebarLayout(
    
    # Inputs: Select variables to plot
    sidebarPanel(
      
      # Set year range
      selectInput(inputId = "year",
                  label = "Select Year Range:",
                  choices = c('', levels(as.factor(dt.trade$year))),
                  selected = '2021',
                  multiple = FALSE),
      sliderInput(inputId = "weight",
                  label = "Select Minimum Trade Value in USD",
                  max = 20000000,
                  min = 0,
                  value = 0,
                  step = 1),
      htmlOutput("textkpi")
    ),
    
    # Output: Show network
    mainPanel(
      tabsetPanel(
        tabPanel("Worldmap Plot",
                 leafletOutput("map"),
                 htmlOutput("textstat")
                  ),
        tabPanel("Network plot", plotOutput("cluster")))
      )
    )
  )


# Define server ----------------------------------------------------------------

server <- function(input, output, session) {
  
  # Load data from file 
  dt.trade <- fread("../../data/cleaned_trade_data.csv") 
  
  output$map <- renderLeaflet({
    # 1. Make edge list
    dt.trade.year <- dt.trade[dt.trade$year == input$year, ]
    dt.trade.year <- dt.trade.year[dt.trade.year$trade_value_usd > input$weight, ]
    dt.trade.edgelist <- dt.trade.year[ , c('reporter_name', 'partner_name')]
    
    # 2. Convert edge list to an igraph network
    # igraph wants our data in matrix format
    m.trade <- as.matrix(dt.trade.edgelist) 
    g.trade <- graph_from_edgelist(m.trade, directed=TRUE)
    
    # 3. Set vertex attributes using set_vertex_attr()
    l.reporters <- as.list(unique(dt.trade.year$reporter_name))
    l.partners <- as.list(unique(dt.trade.year$partner_name))
    l.countries <- as.list(unique(append(l.reporters, l.partners)))
    dt.country.coordinates <- dt.trade.year %>% distinct(partner_name, .keep_all = TRUE)
    dt.country.coordinates <- dt.country.coordinates[, c("partner_name", "partner_lat", "partner_long")]
    meta <- dt.country.coordinates %>% rename("name" = "partner_name", "lat" = "partner_lat", "lon" = "partner_long")

    meta <- meta[match(V(g.trade)$name, meta$name), ]
    V(g.trade)$lat <- meta$lat
    V(g.trade)$lon <- meta$lon
    
    # 4. Set the weight of the edges (trade_value_usd) & add the year as an attribute
    E(g.trade)$weight <- dt.trade.year$trade_value_usd
    # Remove vertices with degree 0
    g.trade <- delete.vertices(g.trade, which(degree(g.trade) == 0))
    
    # 5. Community detection: Walktrap algorithm
    walktrap_communities <- walktrap.community(g.trade, weights = E(g.trade)$weight)
    # Get the community membership vector
    membership_vec <- membership(walktrap_communities)
    # Add the community as an attribute to the vectors
    V(g.trade)$community <- membership_vec[V(g.trade)$name]
    
    # Create a new graph object with community memberships as vertex attributes
    g_comm <- set_vertex_attr(g.trade, "community", value = walktrap_communities$membership)
    # Convert to igraph object
    g_comm <- graph_from_data_frame(get.edgelist(g_comm), directed = TRUE)
    # Compute edge betweenness for the entire graph
    eb <- igraph::edge.betweenness(g_comm)
    # Find the top 10 edges with the highest edge betweenness
    top10_eb <- order(eb, decreasing = TRUE)[1:10]
    # Generate a vector of colors using the rainbow() function
    color_eb <- rainbow(length(E(g_comm)))
    # Set the color of the top 10 edges with highest edge betweenness to red
    color_eb[top10_eb] <- "#000000"
    # Set the color of the remaining edges to transparent
    color_eb[-top10_eb] <- "transparent"
    
    # extract the vertices and edges from the g graph object and store them as a data frame
    gg <- get.data.frame(g.trade, "both")
    # assign vert variable with dataframe info on the vertices + add the spatial coordinates of the points to vert
    vert <- gg$vertices
    coordinates(vert) <- ~lon + lat
    
    # assign edges and weights variable
    edges <- gg$edges
    weights <- E(g.trade)$weight
    
    # Create a list of spatial objects
    edges <- lapply(1:nrow(edges), function(i) 
      {as(rbind(vert[vert$name == edges[i, "from"], ], 
                vert[vert$name == edges[i, "to"], ]), 
          "SpatialLines")
      })
    
    # assign unique IDs to each of the SpatialLines objects
    for (i in seq_along(edges)) {
      edges[[i]] <- spChFIDs(edges[[i]], as.character(i))
    }
    
    # combine all of the SpatialLines objects + new SpatialLinesDataFrame object using the combined SpatialLines object and the edges_df data frame.
    edges <- do.call(rbind, edges)
    edges_df <- data.frame(id = seq_along(edges), weight = weights)
    edges <- SpatialLinesDataFrame(edges, data = edges_df)
    
    # plot map
    community_colors <- hue_pal()(length(unique(vert$community)))
    
    leaflet(vert) %>% 
      addProviderTiles("Stamen.Toner", options = 
                         providerTileOptions(backgroundColor = "#f2f2f2",
                                             opacity = 0.4)) %>% 
      addCircleMarkers(data = vert, radius = 4, 
                   color = community_colors[vert$community], 
                   fillColor = community_colors[vert$community], 
                   fillOpacity = 0.8,
                   stroke = FALSE) %>% 
      addPolylines(data = edges, weight = 2, color = ~color_eb)
  })
  
  output$cluster <- renderPlot({
    # 1. Make edge list
    dt.trade.year <- dt.trade[dt.trade$year == input$year, ]
    dt.trade.year <- dt.trade.year[dt.trade.year$trade_value_usd > input$weight, ]
    dt.trade.edgelist <- dt.trade.year[ , c('reporter_name', 'partner_name')]
    
    # 2. Convert edge list to an igraph network
    # igraph wants our data in matrix format
    m.trade <- as.matrix(dt.trade.edgelist) 
    g.trade <- graph_from_edgelist(m.trade, directed=TRUE)
    
    # 3. Set vertex attributes using set_vertex_attr()
    l.countries <- as.list(unique(dt.trade.year$reporter_name))
    dt.country.coordinates <- dt.trade.year %>% distinct(partner_name, .keep_all = TRUE)
    dt.country.coordinates <- dt.country.coordinates[, c("partner_name", "partner_lat", "partner_long")]
    meta <- dt.country.coordinates %>% rename("name" = "partner_name", "lat" = "partner_lat", "lon" = "partner_long")

    vertex_attrs <- as.list(meta[, -1]) # exclude the 'name' column
    V(g.trade)$lat <- vertex_attrs$lat
    V(g.trade)$lon <- vertex_attrs$lon
    
    # 4. Set the weight of the edges (trade_value_usd) & add the year as an attribute
    E(g.trade)$weight <- dt.trade.year$trade_value_usd
    # Remove vertices with degree 0
    g.trade <- delete.vertices(g.trade, which(degree(g.trade) == 0))
    
    # 5. Community detection algorithms
    # Run Walktrap algorithm
    walktrap_communities <- walktrap.community(g.trade, weights = E(g.trade)$weight)
    
    # Create a new graph object with community memberships as vertex attributes
    g_comm <- set_vertex_attr(g.trade, "community", value = walktrap_communities$membership)
    # Convert to igraph object
    g_comm <- graph_from_data_frame(get.edgelist(g_comm), directed = TRUE)
    # Compute edge betweenness for the entire graph
    eb <- igraph::edge.betweenness(g_comm)
    # Find the top 10 edges with the highest edge betweenness
    top10_eb <- order(eb, decreasing = TRUE)[1:10]
    # Generate a vector of colors using the rainbow() function
    color_eb <- rainbow(length(E(g_comm)))
    # Set the color of the top 10 edges with highest edge betweenness to red
    color_eb[top10_eb] <- "#000000"
    # Set the color of the remaining edges to transparent
    color_eb[-top10_eb] <- "grey"
    
    plot(walktrap_communities, g.trade, vertex.size = 5, vertex.label.cex = 0.3, edge.arrow.size=0.3, vertex.color = walktrap_communities$membership, edge.color = color_eb)
  })
  
  output$textkpi <- renderUI({
    # 1. Make edge list
    dt.trade.year <- dt.trade[dt.trade$year == input$year, ]
    dt.trade.year <- dt.trade.year[dt.trade.year$trade_value_usd > input$weight, ]
    dt.trade.edgelist <- dt.trade.year[ , c('reporter_name', 'partner_name')]
    
    # 2. Convert edge list to an igraph network
    # igraph wants our data in matrix format
    m.trade <- as.matrix(dt.trade.edgelist) 
    g.trade <- graph_from_edgelist(m.trade, directed=TRUE)
    
    # 3. Set vertex attributes using set_vertex_attr()
    l.countries <- as.list(unique(dt.trade.year$reporter_name))
    dt.country.coordinates <- dt.trade.year %>% distinct(partner_name, .keep_all = TRUE)
    dt.country.coordinates <- dt.country.coordinates[, c("partner_name", "partner_lat", "partner_long")]
    meta <- dt.country.coordinates %>% rename("name" = "partner_name", "lat" = "partner_lat", "lon" = "partner_long")
    
    vertex_attrs <- as.list(meta[, -1]) # exclude the 'name' column
    V(g.trade)$lat <- vertex_attrs$lat
    V(g.trade)$lon <- vertex_attrs$lon
    
    # 4. Set the weight of the edges (trade_value_usd) & add the year as an attribute
    E(g.trade)$weight <- dt.trade.year$trade_value_usd
    # Remove vertices with degree 0
    g.trade <- delete.vertices(g.trade, which(degree(g.trade) == 0))
    
    # 5. Community detection: Walktrap algorithm
    walktrap_communities <- walktrap.community(g.trade, weights = E(g.trade)$weight)
    
    # get the average path length for each community
    apl_comm <- lapply(unique(walktrap_communities$membership), function(m) average.path.length(induced_subgraph(g.trade, V(g.trade)[walktrap_communities$membership == m])))
    apl_all <- average.path.length(g.trade)
    
    HTML(paste(" ", "<br>",
               " ", "<br>",
               "Number of communities detected by Walktrap algorithm: ", length(unique(walktrap_communities$membership)), "<br>",
               " ", "<br>",
               "Modularity score of Walktrap algorithm: ", modularity(walktrap_communities, g), "<br>",
               " ", "<br>",
               "Sizes of the communities: ", paste(unname(sizes(walktrap_communities)), collapse = ", "), "<br>",
               " ", "<br>",
               " ", "<br>",
               "Average path length of the total trade network: ", apl_all, "<br>",
               " ", "<br>",
               "Average path length of the communities: ", paste(unname(apl_comm), collapse = ", "), "<br>"))
  })
  
  output$textstat <- renderUI({
    HTML(paste(" ", "<br>",
               " ", "<br>",
               " lalala I need to write text here", "<br>"))
  })
}

# Create a Shiny app object ----------------------------------------------------

shinyApp(ui = ui, server = server)

Listening on http://127.0.0.1:6648
Warning: Error in na.fail.default: missing values in object  106: stop
  105: na.fail.default
  103: model.frame.default
  101: coordinates<-
   99: ::
htmlwidgets
shinyRenderWidget [#60]
   98: func
   85: renderFunc
   84: output$map
    3: runApp
    2: print.shiny.appobj
    1: <Anonymous>
NA
## TRY TO PLOT A MAP
library(data.table)
library(ggplot2)
library(igraph)
library(shiny)
library(DT)             
library(shiny)         
library(ggmap)
library(leaflet)
library(bslib)
library(sf)
library(geosphere)
library(RColorBrewer)
library(sp)
library(dplyr) 


# Load data from file comptab_2018-01-29 16_00_comma_separated.csv 
dt.trade <- fread("../../data/cleaned_trade_data.csv")

    # 1. Make edge list
    dt.trade.year <- dt.trade[dt.trade$year == 2021, ]
    dt.trade.year <- dt.trade.year[dt.trade.year$trade_value_usd > 0, ]
    dt.trade.edgelist <- dt.trade.year[ , c('reporter_name', 'partner_name')]
    
    # 2. Convert edge list to an igraph network
    # igraph wants our data in matrix format
    m.trade <- as.matrix(dt.trade.edgelist) 
    g.trade <- graph_from_edgelist(m.trade, directed=TRUE)
    
    # 3. Set vertex attributes using set_vertex_attr()
    l.reporters <- as.list(unique(dt.trade.year$reporter_name))
    l.partners <- as.list(unique(dt.trade.year$partner_name))
    l.countries <- as.list(unique(append(l.reporters, l.partners)))
    dt.country.coordinates <- dt.trade.year %>% distinct(partner_name, .keep_all = TRUE)
    dt.country.coordinates <- dt.country.coordinates[, c("partner_name", "partner_lat", "partner_long")]
    meta <- dt.country.coordinates %>% rename("name" = "partner_name", "lat" = "partner_lat", "lon" = "partner_long")

    meta <- meta[match(V(g.trade)$name, meta$name), ]
    V(g.trade)$lat <- meta$lat
    V(g.trade)$lon <- meta$lon
    
    # 4. Set the weight of the edges (trade_value_usd) & add the year as an attribute
    E(g.trade)$weight <- dt.trade.year$trade_value_usd
    # Remove vertices with degree 0
    g.trade <- delete.vertices(g.trade, which(degree(g.trade) == 0))
    
    # 5. Community detection algorithms
    # Run Walktrap algorithm
    walktrap_communities <- walktrap.community(g.trade, weights = E(g.trade)$weight)
    # Get the community membership vector
    membership_vec <- membership(walktrap_communities)
    # Add the community as an attribute to the vectors
    V(g.trade)$community <- membership_vec[V(g.trade)$name]
    
    # Create a new graph object with community memberships as vertex attributes
    g_comm <- set_vertex_attr(g.trade, "community", value = walktrap_communities$membership)
    # Convert to igraph object
    g_comm <- graph_from_data_frame(get.edgelist(g_comm), directed = TRUE)
    # Compute edge betweenness for the entire graph
    eb <- igraph::edge.betweenness(g_comm)
    # Find the top 10 edges with the highest edge betweenness
    top10_eb <- order(eb, decreasing = TRUE)[1:length(unique(walktrap_communities$membership))]
    # Generate a vector of colors using the rainbow() function
    color_eb <- rainbow(length(E(g_comm)))
    # Set the color of the top 10 edges with highest edge betweenness to red
    color_eb[top10_eb] <- "red"
    # Set the color of the remaining edges to transparent
    color_eb[-top10_eb] <- "transparent"
    
    # extract the vertices and edges from the g graph object and store them as a data frame
    gg <- get.data.frame(g.trade, "both")
    # assign vert variable with dataframe info on the vertices + add the spatial coordinates of the points to vert
    vert <- gg$vertices
    coordinates(vert) <- ~lon + lat
    # assign edges and weights variable
    edges <- gg$edges
    weights <- E(g.trade)$weight
    
    # Create a list of spatial objects
    edges <- lapply(1:nrow(edges), function(i) 
      {as(rbind(vert[vert$name == edges[i, "from"], ], 
                vert[vert$name == edges[i, "to"], ]), 
          "SpatialLines")
      })
    # assign unique IDs to each of the SpatialLines objects
    for (i in seq_along(edges)) {
      edges[[i]] <- spChFIDs(edges[[i]], as.character(i))
    }
    # combine all of the SpatialLines objects + new SpatialLinesDataFrame object using the combined SpatialLines object and the edges_df data frame.
    edges <- do.call(rbind, edges)
    edges_df <- data.frame(id = seq_along(edges), weight = weights)
    edges <- SpatialLinesDataFrame(edges, data = edges_df)
    
    # plot map
    community_colors <- c("blue", "green", "red")
    
    leaflet(vert) %>% 
      addTiles() %>% 
      addCircleMarkers(data = vert, radius = 2, 
                   color = community_colors[vert$community], 
                   fillColor = community_colors[vert$community], 
                   fillOpacity = 0.8,
                   stroke = FALSE) %>% 
      addPolylines(data = edges, weight = 2, color = ~color_eb)
NA
LS0tCnRpdGxlOiAiUiBOb3RlYm9vayIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKCgpgYGB7cn0KbGlicmFyeShkYXRhLnRhYmxlKSAgICAgIyBSdW4gb25jZSBwZXIgc2Vzc2lvbgpsaWJyYXJ5KGdncGxvdDIpICAgICAgICAjIFJ1biBvbmNlIHBlciBzZXNzaW9uCmxpYnJhcnkoaWdyYXBoKSAgICAgICAgICMgUnVuIG9uY2UgcGVyIHNlc3Npb24KCmR0LnRyYWRlIDwtIGZyZWFkKCIuLi8uLi9kYXRhL2NsZWFuZWRfdHJhZGVfZGF0YS5jc3YiKQpkdC50cmFkZQpgYGAKCgpgYGB7cn0KIyBTZXQgdGhlIHBhcmFtZXRlcnMKZHQudHJhZGUuY291bnRyeSA8LSBkdC50cmFkZVtyZXBvcnRlcl9uYW1lID09ICJBZmdoYW5pc3RhbiJdCmR0LnRyYWRlLmNvdW50cnkueWVhciA8LSBkdC50cmFkZS5jb3VudHJ5W3llYXIgPT0gMjAxN10KCiMgU2V0IHRoZSB2ZXJ0aWNlcwpkdC5hbGwucmVwb3J0ZXJzIDwtIGR0LnRyYWRlLmNvdW50cnkueWVhclssIGxpc3QobmFtZT11bmlxdWUocmVwb3J0ZXJfbmFtZSksIHR5cGU9RkFMU0UpXQpkdC5hbGwucGFydG5lcnMgPC0gZHQudHJhZGUuY291bnRyeS55ZWFyWywgbGlzdChuYW1lPXVuaXF1ZShwYXJ0bmVyX25hbWUpLCB0eXBlPVRSVUUpXQoKZHQuYWxsLnZlcnRpY2VzIDwtIHJiaW5kKGR0LmFsbC5yZXBvcnRlcnMsIGR0LmFsbC5wYXJ0bmVycykKCiMgQ3JlYXRlICYgcGxvdCB0aGUgZ3JhcGgKZyA8LSBncmFwaC5kYXRhLmZyYW1lKGR0LnRyYWRlLmNvdW50cnkueWVhclssIGxpc3QocmVwb3J0ZXJfbmFtZSwgcGFydG5lcl9uYW1lKV0sIGRpcmVjdGVkPVRSVUUsIHZlcnRpY2VzPWR0LmFsbC52ZXJ0aWNlcykKCnBsb3QoZywgdmVydGV4LnNpemUgPSA1LCB2ZXJ0ZXgubGFiZWwuY2V4ID0gMC42KQoKIyBBZGQgd2VpZ2h0cyB0byB0aGUgZ3JhcGggJiBwbG90CmcgPC0gc2V0X2VkZ2VfYXR0cihnLCAid2VpZ2h0IiwgdmFsdWU9IGR0LnRyYWRlLmNvdW50cnkueWVhciR0cmFkZV92YWx1ZV91c2QpCgpwbG90KGcsIHZlcnRleC5zaXplID0gNSwgdmVydGV4LmxhYmVsLmNleCA9IDAuNikKYGBgCgoKYGBge3J9CiMgMS4gTWFrZSBlZGdlIGxpc3QKZHQudHJhZGUueWVhciA8LSBkdC50cmFkZVt5ZWFyID09IDIwMjFdCmR0LnRyYWRlLnllYXIKZHQudHJhZGUuZWRnZWxpc3QgPC0gZHQudHJhZGUueWVhclsgLCBjKCdyZXBvcnRlcl9uYW1lJywgJ3BhcnRuZXJfbmFtZScpXQpkdC50cmFkZS5lZGdlbGlzdAoKCiMgMi4gQ29udmVydCBlZGdlIGxpc3QgdG8gYW4gaWdyYXBoIG5ldHdvcmsKIyBpZ3JhcGggd2FudHMgb3VyIGRhdGEgaW4gbWF0cml4IGZvcm1hdAptLnRyYWRlIDwtIGFzLm1hdHJpeChkdC50cmFkZS5lZGdlbGlzdCkgCmcudHJhZGUgPC0gZ3JhcGhfZnJvbV9lZGdlbGlzdChtLnRyYWRlLCBkaXJlY3RlZD1UUlVFKQoKCiMgMy4gU2V0IHRoZSB3ZWlnaHQgb2YgdGhlIGVkZ2VzICh0cmFkZV92YWx1ZV91c2QpICYgYWRkIHRoZSB5ZWFyIGFzIGFuIGF0dHJpYnV0ZQpFKGcudHJhZGUpJHdlaWdodCA8LSBkdC50cmFkZS55ZWFyJHRyYWRlX3ZhbHVlX3VzZAogICMgU3VtIHRoZSB3ZWlnaHRzIGZvciBkdXBsaWNhdGUgZWRnZXMgKHdlIHdvdWxkIG5lZWQgdGhpcyBpZiB3ZSBzZWxlY3QgbXVsdGlwbGUgeWVhcnMpCiAgZy50cmFkZSA8LSBzaW1wbGlmeShnLnRyYWRlLCByZW1vdmUubXVsdGlwbGUgPSBUUlVFLCBlZGdlLmF0dHIuY29tYj1saXN0KHdlaWdodD0ic3VtIiwgeWVhcj0iY29uY2F0IikpCgojIDQuIEFkZCBhdHRyaWJ1dGVzIHRvIHRoZSB2ZXJ0aWNlcyAoY29udGluZW50KQpsaWJyYXJ5KHJqc29uKQpqc29uX2RhdGEgPC0gZnJvbUpTT04oZmlsZT0nLi4vLi4vZGF0YS9zYW1wbGUuanNvbicpClYoZy50cmFkZSkkY29udGluZW50IDwtICB1bmxpc3QoanNvbl9kYXRhW1YoZy50cmFkZSkkbmFtZV0pCgojIDUuIFBsb3QgaWdyYXBoCnBsb3QoZy50cmFkZSwgdmVydGV4LnNpemUgPSA1LCB2ZXJ0ZXgubGFiZWwuY2V4ID0gMC4zLCBlZGdlLmFycm93LnNpemU9MC40KQpgYGAKCgpgYGB7cn0KIyA2LiBDb21tdW5pdHkgZGV0ZWN0aW9uIGFsZ29yaXRobXMKIyBSdW4gV2Fsa3RyYXAgYWxnb3JpdGhtCndhbGt0cmFwX2NvbW11bml0aWVzIDwtIHdhbGt0cmFwLmNvbW11bml0eShnLnRyYWRlLCB3ZWlnaHRzID0gRShnLnRyYWRlKSR3ZWlnaHQpCiMgUHJpbnQgdGhlIG51bWJlciBvZiBjb21tdW5pdGllcyBkZXRlY3RlZApjYXQoIk51bWJlciBvZiBjb21tdW5pdGllcyBkZXRlY3RlZCBieSBXYWxrdHJhcCBhbGdvcml0aG06ICIsIGxlbmd0aCh1bmlxdWUod2Fsa3RyYXBfY29tbXVuaXRpZXMkbWVtYmVyc2hpcCkpLCAiXG4iKQojIFBsb3QgdGhlIHJlc3VsdGluZyBjbHVzdGVycwpwbG90KHdhbGt0cmFwX2NvbW11bml0aWVzLCBnLnRyYWRlLCB2ZXJ0ZXguc2l6ZSA9IDUsIHZlcnRleC5sYWJlbC5jZXggPSAwLjMsIGVkZ2UuYXJyb3cuc2l6ZT0wLjQsIHZlcnRleC5jb2xvciA9IHdhbGt0cmFwX2NvbW11bml0aWVzJG1lbWJlcnNoaXApCgojTW9kdWxhcml0eSBzY29yZTogT25lIHdheSB0byBldmFsdWF0ZSB0aGUgcXVhbGl0eSBvZiB0aGUgY2x1c3RlcmluZyBpcyB0byBjYWxjdWxhdGUgdGhlIG1vZHVsYXJpdHkgc2NvcmUsIHdoaWNoIG1lYXN1cmVzIHRoZSBleHRlbnQgdG8gd2hpY2ggdGhlIG5vZGVzIHdpdGhpbiBhIGNvbW11bml0eSBhcmUgbW9yZSBkZW5zZWx5IGNvbm5lY3RlZCB0byBlYWNoIG90aGVyIHRoYW4gdG8gbm9kZXMgb3V0c2lkZSB0aGUgY29tbXVuaXR5LiAKbW9kdWxhcml0eSh3YWxrdHJhcF9jb21tdW5pdGllcywgZykKCiMgZ2V0IHRoZSBzaXplIG9mIGVhY2ggY29tbXVuaXR5CnNpemVzKHdhbGt0cmFwX2NvbW11bml0aWVzKQoKIyBDcmVhdGUgYSBuZXcgZ3JhcGggb2JqZWN0IHdpdGggY29tbXVuaXR5IG1lbWJlcnNoaXBzIGFzIHZlcnRleCBhdHRyaWJ1dGVzCmdfY29tbSA8LSBzZXRfdmVydGV4X2F0dHIoZy50cmFkZSwgImNvbW11bml0eSIsIHZhbHVlID0gd2Fsa3RyYXBfY29tbXVuaXRpZXMkbWVtYmVyc2hpcCkKIyBDb252ZXJ0IHRvIGlncmFwaCBvYmplY3QKZ19jb21tIDwtIGdyYXBoX2Zyb21fZGF0YV9mcmFtZShnZXQuZWRnZWxpc3QoZ19jb21tKSwgZGlyZWN0ZWQgPSBUUlVFKQojIENvbXB1dGUgZWRnZSBiZXR3ZWVubmVzcyBmb3IgdGhlIGVudGlyZSBncmFwaAplYiA8LSBpZ3JhcGg6OmVkZ2UuYmV0d2Vlbm5lc3MoZykKIyBGaW5kIHRoZSB0b3AgMTAgZWRnZXMgd2l0aCB0aGUgaGlnaGVzdCBlZGdlIGJldHdlZW5uZXNzCnRvcDEwX2ViIDwtIG9yZGVyKGViLCBkZWNyZWFzaW5nID0gVFJVRSlbMToxMF0KIyBHZW5lcmF0ZSBhIHZlY3RvciBvZiBjb2xvcnMgdXNpbmcgdGhlIHJhaW5ib3coKSBmdW5jdGlvbgpjb2xvcl9lYiA8LSByYWluYm93KGxlbmd0aChFKGcpKSkKIyBTZXQgdGhlIGNvbG9yIG9mIHRoZSB0b3AgMTAgZWRnZXMgd2l0aCBoaWdoZXN0IGVkZ2UgYmV0d2Vlbm5lc3MgdG8gcmVkCmNvbG9yX2ViW3RvcDEwX2ViXSA8LSAicmVkIgojIFNldCB0aGUgY29sb3Igb2YgdGhlIHJlbWFpbmluZyBlZGdlcyB0byB0cmFuc3BhcmVudApjb2xvcl9lYlstdG9wMTBfZWJdIDwtICJ0cmFuc3BhcmVudCIKCiMgZ2V0IHRoZSBhdmVyYWdlIHBhdGggbGVuZ3RoIGZvciBlYWNoIGNvbW11bml0eQphcGxfY29tbSA8LSBsYXBwbHkodW5pcXVlKHdhbGt0cmFwX2NvbW11bml0aWVzJG1lbWJlcnNoaXApLCBmdW5jdGlvbihtKSBhdmVyYWdlLnBhdGgubGVuZ3RoKGluZHVjZWRfc3ViZ3JhcGgoZy50cmFkZSwgVihnLnRyYWRlKVt3YWxrdHJhcF9jb21tdW5pdGllcyRtZW1iZXJzaGlwID09IG1dKSkpCmFwbF9hbGwgPC0gYXZlcmFnZS5wYXRoLmxlbmd0aChnLnRyYWRlKQpwcmludChhcGxfYWxsKQpwcmludChhcGxfY29tbSkKCnBsb3QoZy50cmFkZSwgdmVydGV4LnNpemUgPSA1LCB2ZXJ0ZXgubGFiZWwuY2V4ID0gMC4zLCBlZGdlLmFycm93LnNpemU9MC40LCB2ZXJ0ZXguY29sb3IgPSB3YWxrdHJhcF9jb21tdW5pdGllcyRtZW1iZXJzaGlwLCBlZGdlLmNvbG9yID0gY29sb3JfZWIpCgpgYGAKCgpgYGB7cn0KIyBTSElOWSBXSVRIT1VUIFdFSUdUSCBNSU4gQU5EIFdJVEhPVVQgTUFQCgpsaWJyYXJ5KGRhdGEudGFibGUpCmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeShpZ3JhcGgpCmxpYnJhcnkoc2hpbnkpCgoKIyBMb2FkIGRhdGEgZnJvbSBmaWxlIGNvbXB0YWJfMjAxOC0wMS0yOSAxNl8wMF9jb21tYV9zZXBhcmF0ZWQuY3N2IApkdC50cmFkZSA8LSBmcmVhZCgiLi4vLi4vZGF0YS9jbGVhbmVkX3RyYWRlX2RhdGEuY3N2IikgCgojIERlZmluZSBVSSAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKdWkgPC0gZmx1aWRQYWdlKAogIAogICMgQXBwIHRpdGxlIC0tLS0KICB0aXRsZVBhbmVsKCJTaGlueSBBcHAgZm9yIEludGVybmF0aW9uYWwgVHJhZGUiKSwKICAKICBzaWRlYmFyTGF5b3V0KAogICAgCiAgICAjIElucHV0czogU2VsZWN0IHZhcmlhYmxlcyB0byBwbG90CiAgICBzaWRlYmFyUGFuZWwoCiAgICAgIAogICAgICAjIFNldCB5ZWFyIHJhbmdlCiAgICAgIHNlbGVjdElucHV0KGlucHV0SWQgPSAieWVhciIsCiAgICAgICAgICAgICAgICAgIGxhYmVsID0gIlNlbGVjdCBZZWFyIFJhbmdlOiIsCiAgICAgICAgICAgICAgICAgIGNob2ljZXMgPSBjKCcnLCBsZXZlbHMoYXMuZmFjdG9yKGR0LnRyYWRlJHllYXIpKSksCiAgICAgICAgICAgICAgICAgIHNlbGVjdGVkID0gJzIwMjEnLAogICAgICAgICAgICAgICAgICBtdWx0aXBsZSA9IEZBTFNFKQogICAgKSwKICAgIAogICAgCiAgICAjIE91dHB1dDogU2hvdyBuZXR3b3JrCiAgICBtYWluUGFuZWwoCiAgICAgIHBsb3RPdXRwdXQoImNsdXN0ZXIiKSwKICAgICAgaHRtbE91dHB1dCgidGV4dCIpCiAgICApCiAgKQopCgoKIyBEZWZpbmUgc2VydmVyIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCnNlcnZlciA8LSBmdW5jdGlvbihpbnB1dCwgb3V0cHV0LCBzZXNzaW9uKSB7CiAgCiAgb3V0cHV0JGNsdXN0ZXIgPC0gcmVuZGVyUGxvdCh7CiAgICAKICAgICMgMS4gTWFrZSBlZGdlIGxpc3QKICAgIGR0LnRyYWRlLnllYXIgPC0gZHQudHJhZGVbZHQudHJhZGUkeWVhciA9PSBpbnB1dCR5ZWFyLCBdCiAgICBkdC50cmFkZS5lZGdlbGlzdCA8LSBkdC50cmFkZS55ZWFyWyAsIGMoJ3JlcG9ydGVyX25hbWUnLCAncGFydG5lcl9uYW1lJyldCiAgICAKICAgICMgMi4gQ29udmVydCBlZGdlIGxpc3QgdG8gYW4gaWdyYXBoIG5ldHdvcmsKICAgICMgaWdyYXBoIHdhbnRzIG91ciBkYXRhIGluIG1hdHJpeCBmb3JtYXQKICAgIG0udHJhZGUgPC0gYXMubWF0cml4KGR0LnRyYWRlLmVkZ2VsaXN0KSAKICAgIGcudHJhZGUgPC0gZ3JhcGhfZnJvbV9lZGdlbGlzdChtLnRyYWRlLCBkaXJlY3RlZD1UUlVFKQogICAgCiAgICAjIDMuIFNldCB0aGUgd2VpZ2h0IG9mIHRoZSBlZGdlcyAodHJhZGVfdmFsdWVfdXNkKSAmIGFkZCB0aGUgeWVhciBhcyBhbiBhdHRyaWJ1dGUKICAgIEUoZy50cmFkZSkkd2VpZ2h0IDwtIGR0LnRyYWRlLnllYXIkdHJhZGVfdmFsdWVfdXNkCiAgICAKICAgICMgNC4gQ29tbXVuaXR5IGRldGVjdGlvbiBhbGdvcml0aG1zCiAgICAjIFJ1biBXYWxrdHJhcCBhbGdvcml0aG0KICAgIHdhbGt0cmFwX2NvbW11bml0aWVzIDwtIHdhbGt0cmFwLmNvbW11bml0eShnLnRyYWRlLCB3ZWlnaHRzID0gRShnLnRyYWRlKSR3ZWlnaHQpCiAgICAKICAgICMgQ3JlYXRlIGEgbmV3IGdyYXBoIG9iamVjdCB3aXRoIGNvbW11bml0eSBtZW1iZXJzaGlwcyBhcyB2ZXJ0ZXggYXR0cmlidXRlcwogICAgZ19jb21tIDwtIHNldF92ZXJ0ZXhfYXR0cihnLnRyYWRlLCAiY29tbXVuaXR5IiwgdmFsdWUgPSB3YWxrdHJhcF9jb21tdW5pdGllcyRtZW1iZXJzaGlwKQogICAgIyBDb252ZXJ0IHRvIGlncmFwaCBvYmplY3QKICAgIGdfY29tbSA8LSBncmFwaF9mcm9tX2RhdGFfZnJhbWUoZ2V0LmVkZ2VsaXN0KGdfY29tbSksIGRpcmVjdGVkID0gVFJVRSkKICAgICMgQ29tcHV0ZSBlZGdlIGJldHdlZW5uZXNzIGZvciB0aGUgZW50aXJlIGdyYXBoCiAgICBlYiA8LSBpZ3JhcGg6OmVkZ2UuYmV0d2Vlbm5lc3MoZ19jb21tKQogICAgIyBGaW5kIHRoZSB0b3AgMTAgZWRnZXMgd2l0aCB0aGUgaGlnaGVzdCBlZGdlIGJldHdlZW5uZXNzCiAgICB0b3AxMF9lYiA8LSBvcmRlcihlYiwgZGVjcmVhc2luZyA9IFRSVUUpWzE6MTBdCiAgICAjIEdlbmVyYXRlIGEgdmVjdG9yIG9mIGNvbG9ycyB1c2luZyB0aGUgcmFpbmJvdygpIGZ1bmN0aW9uCiAgICBjb2xvcl9lYiA8LSByYWluYm93KGxlbmd0aChFKGdfY29tbSkpKQogICAgIyBTZXQgdGhlIGNvbG9yIG9mIHRoZSB0b3AgMTAgZWRnZXMgd2l0aCBoaWdoZXN0IGVkZ2UgYmV0d2Vlbm5lc3MgdG8gcmVkCiAgICBjb2xvcl9lYlt0b3AxMF9lYl0gPC0gInJlZCIKICAgICMgU2V0IHRoZSBjb2xvciBvZiB0aGUgcmVtYWluaW5nIGVkZ2VzIHRvIHRyYW5zcGFyZW50CiAgICBjb2xvcl9lYlstdG9wMTBfZWJdIDwtICJ0cmFuc3BhcmVudCIKICAgIAogICAgcGxvdCh3YWxrdHJhcF9jb21tdW5pdGllcywgZy50cmFkZSwgdmVydGV4LnNpemUgPSA1LCB2ZXJ0ZXgubGFiZWwuY2V4ID0gMC4zLCBlZGdlLmFycm93LnNpemU9MC40LCB2ZXJ0ZXguY29sb3IgPSB3YWxrdHJhcF9jb21tdW5pdGllcyRtZW1iZXJzaGlwLCBlZGdlLmNvbG9yID0gY29sb3JfZWIpCiAgICAKICB9KQogIAogIG91dHB1dCR0ZXh0IDwtIHJlbmRlclVJKHsKICAgICMgMS4gTWFrZSBlZGdlIGxpc3QKICAgIGR0LnRyYWRlLnllYXIgPC0gZHQudHJhZGVbZHQudHJhZGUkeWVhciA9PSBpbnB1dCR5ZWFyLCBdCiAgICBkdC50cmFkZS5lZGdlbGlzdCA8LSBkdC50cmFkZS55ZWFyWyAsIGMoJ3JlcG9ydGVyX25hbWUnLCAncGFydG5lcl9uYW1lJyldCiAgICAKICAgICMgMi4gQ29udmVydCBlZGdlIGxpc3QgdG8gYW4gaWdyYXBoIG5ldHdvcmsKICAgICMgaWdyYXBoIHdhbnRzIG91ciBkYXRhIGluIG1hdHJpeCBmb3JtYXQKICAgIG0udHJhZGUgPC0gYXMubWF0cml4KGR0LnRyYWRlLmVkZ2VsaXN0KSAKICAgIGcudHJhZGUgPC0gZ3JhcGhfZnJvbV9lZGdlbGlzdChtLnRyYWRlLCBkaXJlY3RlZD1UUlVFKQogICAgCiAgICAjIDMuIFNldCB0aGUgd2VpZ2h0IG9mIHRoZSBlZGdlcyAodHJhZGVfdmFsdWVfdXNkKSAmIGFkZCB0aGUgeWVhciBhcyBhbiBhdHRyaWJ1dGUKICAgIEUoZy50cmFkZSkkd2VpZ2h0IDwtIGR0LnRyYWRlLnllYXIkdHJhZGVfdmFsdWVfdXNkCiAgICAKICAgICMgNi4gQ29tbXVuaXR5IGRldGVjdGlvbiBhbGdvcml0aG1zCiAgICAjIFJ1biBXYWxrdHJhcCBhbGdvcml0aG0KICAgIHdhbGt0cmFwX2NvbW11bml0aWVzIDwtIHdhbGt0cmFwLmNvbW11bml0eShnLnRyYWRlLCB3ZWlnaHRzID0gRShnLnRyYWRlKSR3ZWlnaHQpCiAgICAKICAgICMgZ2V0IHRoZSBhdmVyYWdlIHBhdGggbGVuZ3RoIGZvciBlYWNoIGNvbW11bml0eQogICAgYXBsX2NvbW0gPC0gbGFwcGx5KHVuaXF1ZSh3YWxrdHJhcF9jb21tdW5pdGllcyRtZW1iZXJzaGlwKSwgZnVuY3Rpb24obSkgYXZlcmFnZS5wYXRoLmxlbmd0aChpbmR1Y2VkX3N1YmdyYXBoKGcudHJhZGUsIFYoZy50cmFkZSlbd2Fsa3RyYXBfY29tbXVuaXRpZXMkbWVtYmVyc2hpcCA9PSBtXSkpKQogICAgYXBsX2FsbCA8LSBhdmVyYWdlLnBhdGgubGVuZ3RoKGcudHJhZGUpCiAgICAKICAgIEhUTUwocGFzdGUoIk51bWJlciBvZiBjb21tdW5pdGllcyBkZXRlY3RlZCBieSBXYWxrdHJhcCBhbGdvcml0aG06ICIsIGxlbmd0aCh1bmlxdWUod2Fsa3RyYXBfY29tbXVuaXRpZXMkbWVtYmVyc2hpcCkpLCAiPGJyPiIsCiAgICAgICAgICAgICAiTW9kdWxhcml0eSBzY29yZSBvZiBXYWxrdHJhcCBhbGdvcml0aG06ICIsIG1vZHVsYXJpdHkod2Fsa3RyYXBfY29tbXVuaXRpZXMsIGcpLCAiPGJyPiIsCiAgICAgICAgICAgICAiU2l6ZXMgb2YgdGhlIGNvbW11bml0aWVzOiAiLCBwYXN0ZSh1bm5hbWUoc2l6ZXMod2Fsa3RyYXBfY29tbXVuaXRpZXMpKSwgY29sbGFwc2UgPSAiLCAiKSwgIjxicj4iLAogICAgICAgICAgICAgIiAiLCAiPGJyPiIsCiAgICAgICAgICAgICAiQXZlcmFnZSBwYXRoIGxlbmd0aCBvZiB0aGUgdG90YWwgdHJhZGUgbmV0d29yazogIiwgYXBsX2FsbCwgIjxicj4iLAogICAgICAgICAgICAgIkF2ZXJhZ2UgcGF0aCBsZW5ndGggb2YgdGhlIGNvbW11bml0aWVzOiAiLCBwYXN0ZSh1bm5hbWUoYXBsX2NvbW0pLCBjb2xsYXBzZSA9ICIsICIpLCAiPGJyPiIpKQogIH0pCn0KCiMgQ3JlYXRlIGEgU2hpbnkgYXBwIG9iamVjdCAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgpzaGlueUFwcCh1aSA9IHVpLCBzZXJ2ZXIgPSBzZXJ2ZXIpCmBgYAoKCmBgYHtyfQojIFNISU5ZIFdJVEggV0VJR0hUIE1JTiBBTkQgTUFQCmxpYnJhcnkoZGF0YS50YWJsZSkKbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KHNjYWxlcykKbGlicmFyeShpZ3JhcGgpCmxpYnJhcnkoc2hpbnkpCmxpYnJhcnkoc2hpbnlXaWRnZXRzKQpsaWJyYXJ5KERUKSAgICAgICAgICAgICAKbGlicmFyeShzaGlueSkgICAgICAgICAKbGlicmFyeShnZ21hcCkKbGlicmFyeShsZWFmbGV0KQpsaWJyYXJ5KGJzbGliKQpsaWJyYXJ5KHNmKQpsaWJyYXJ5KGdlb3NwaGVyZSkKbGlicmFyeShSQ29sb3JCcmV3ZXIpCmxpYnJhcnkoc3ApCmxpYnJhcnkoZHBseXIpCmxpYnJhcnkocmVtb3RlcykKbGlicmFyeShtYWdyaXR0cikKbGlicmFyeShkZXZ0b29scykKCmR0LnRyYWRlIDwtIGZyZWFkKCIuLi8uLi9kYXRhL2NsZWFuZWRfdHJhZGVfZGF0YS5jc3YiKQoKIyBEZWZpbmUgVUkgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCnVpIDwtIGZsdWlkUGFnZSgKICAKICAjIEFwcCB0aXRsZSAtLS0tCiAgdGl0bGVQYW5lbCgiU2hpbnkgQXBwIGZvciBJbnRlcm5hdGlvbmFsIFRyYWRlIiksCiAgCiAgc2lkZWJhckxheW91dCgKICAgIAogICAgIyBJbnB1dHM6IFNlbGVjdCB2YXJpYWJsZXMgdG8gcGxvdAogICAgc2lkZWJhclBhbmVsKAogICAgICAKICAgICAgIyBTZXQgeWVhciByYW5nZQogICAgICBzZWxlY3RJbnB1dChpbnB1dElkID0gInllYXIiLAogICAgICAgICAgICAgICAgICBsYWJlbCA9ICJTZWxlY3QgWWVhciBSYW5nZToiLAogICAgICAgICAgICAgICAgICBjaG9pY2VzID0gYygnJywgbGV2ZWxzKGFzLmZhY3RvcihkdC50cmFkZSR5ZWFyKSkpLAogICAgICAgICAgICAgICAgICBzZWxlY3RlZCA9ICcyMDIxJywKICAgICAgICAgICAgICAgICAgbXVsdGlwbGUgPSBGQUxTRSksCiAgICAgIHNsaWRlcklucHV0KGlucHV0SWQgPSAid2VpZ2h0IiwKICAgICAgICAgICAgICAgICAgbGFiZWwgPSAiU2VsZWN0IE1pbmltdW0gVHJhZGUgVmFsdWUgaW4gVVNEIiwKICAgICAgICAgICAgICAgICAgbWF4ID0gMjAwMDAwMDAsCiAgICAgICAgICAgICAgICAgIG1pbiA9IDAsCiAgICAgICAgICAgICAgICAgIHZhbHVlID0gMCwKICAgICAgICAgICAgICAgICAgc3RlcCA9IDEpLAogICAgICBodG1sT3V0cHV0KCJ0ZXh0a3BpIikKICAgICksCiAgICAKICAgICMgT3V0cHV0OiBTaG93IG5ldHdvcmsKICAgIG1haW5QYW5lbCgKICAgICAgdGFic2V0UGFuZWwoCiAgICAgICAgdGFiUGFuZWwoIldvcmxkbWFwIFBsb3QiLAogICAgICAgICAgICAgICAgIGxlYWZsZXRPdXRwdXQoIm1hcCIpLAogICAgICAgICAgICAgICAgIGh0bWxPdXRwdXQoInRleHRzdGF0IikKICAgICAgICAgICAgICAgICAgKSwKICAgICAgICB0YWJQYW5lbCgiTmV0d29yayBwbG90IiwgcGxvdE91dHB1dCgiY2x1c3RlciIpKSkKICAgICAgKQogICAgKQogICkKCgojIERlZmluZSBzZXJ2ZXIgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKc2VydmVyIDwtIGZ1bmN0aW9uKGlucHV0LCBvdXRwdXQsIHNlc3Npb24pIHsKICAKICAjIExvYWQgZGF0YSBmcm9tIGZpbGUgCiAgZHQudHJhZGUgPC0gZnJlYWQoIi4uLy4uL2RhdGEvY2xlYW5lZF90cmFkZV9kYXRhLmNzdiIpIAogIAogIG91dHB1dCRtYXAgPC0gcmVuZGVyTGVhZmxldCh7CiAgICAjIDEuIE1ha2UgZWRnZSBsaXN0CiAgICBkdC50cmFkZS55ZWFyIDwtIGR0LnRyYWRlW2R0LnRyYWRlJHllYXIgPT0gaW5wdXQkeWVhciwgXQogICAgZHQudHJhZGUueWVhciA8LSBkdC50cmFkZS55ZWFyW2R0LnRyYWRlLnllYXIkdHJhZGVfdmFsdWVfdXNkID4gaW5wdXQkd2VpZ2h0LCBdCiAgICBkdC50cmFkZS5lZGdlbGlzdCA8LSBkdC50cmFkZS55ZWFyWyAsIGMoJ3JlcG9ydGVyX25hbWUnLCAncGFydG5lcl9uYW1lJyldCiAgICAKICAgICMgMi4gQ29udmVydCBlZGdlIGxpc3QgdG8gYW4gaWdyYXBoIG5ldHdvcmsKICAgICMgaWdyYXBoIHdhbnRzIG91ciBkYXRhIGluIG1hdHJpeCBmb3JtYXQKICAgIG0udHJhZGUgPC0gYXMubWF0cml4KGR0LnRyYWRlLmVkZ2VsaXN0KSAKICAgIGcudHJhZGUgPC0gZ3JhcGhfZnJvbV9lZGdlbGlzdChtLnRyYWRlLCBkaXJlY3RlZD1UUlVFKQogICAgCiAgICAjIDMuIFNldCB2ZXJ0ZXggYXR0cmlidXRlcyB1c2luZyBzZXRfdmVydGV4X2F0dHIoKQogICAgbC5yZXBvcnRlcnMgPC0gYXMubGlzdCh1bmlxdWUoZHQudHJhZGUueWVhciRyZXBvcnRlcl9uYW1lKSkKICAgIGwucGFydG5lcnMgPC0gYXMubGlzdCh1bmlxdWUoZHQudHJhZGUueWVhciRwYXJ0bmVyX25hbWUpKQogICAgbC5jb3VudHJpZXMgPC0gYXMubGlzdCh1bmlxdWUoYXBwZW5kKGwucmVwb3J0ZXJzLCBsLnBhcnRuZXJzKSkpCiAgICBkdC5jb3VudHJ5LmNvb3JkaW5hdGVzIDwtIGR0LnRyYWRlLnllYXIgJT4lIGRpc3RpbmN0KHBhcnRuZXJfbmFtZSwgLmtlZXBfYWxsID0gVFJVRSkKICAgIGR0LmNvdW50cnkuY29vcmRpbmF0ZXMgPC0gZHQuY291bnRyeS5jb29yZGluYXRlc1ssIGMoInBhcnRuZXJfbmFtZSIsICJwYXJ0bmVyX2xhdCIsICJwYXJ0bmVyX2xvbmciKV0KICAgIG1ldGEgPC0gZHQuY291bnRyeS5jb29yZGluYXRlcyAlPiUgcmVuYW1lKCJuYW1lIiA9ICJwYXJ0bmVyX25hbWUiLCAibGF0IiA9ICJwYXJ0bmVyX2xhdCIsICJsb24iID0gInBhcnRuZXJfbG9uZyIpCgogICAgbWV0YSA8LSBtZXRhW21hdGNoKFYoZy50cmFkZSkkbmFtZSwgbWV0YSRuYW1lKSwgXQogICAgVihnLnRyYWRlKSRsYXQgPC0gbWV0YSRsYXQKICAgIFYoZy50cmFkZSkkbG9uIDwtIG1ldGEkbG9uCiAgICAKICAgICMgNC4gU2V0IHRoZSB3ZWlnaHQgb2YgdGhlIGVkZ2VzICh0cmFkZV92YWx1ZV91c2QpICYgYWRkIHRoZSB5ZWFyIGFzIGFuIGF0dHJpYnV0ZQogICAgRShnLnRyYWRlKSR3ZWlnaHQgPC0gZHQudHJhZGUueWVhciR0cmFkZV92YWx1ZV91c2QKICAgICMgUmVtb3ZlIHZlcnRpY2VzIHdpdGggZGVncmVlIDAKICAgIGcudHJhZGUgPC0gZGVsZXRlLnZlcnRpY2VzKGcudHJhZGUsIHdoaWNoKGRlZ3JlZShnLnRyYWRlKSA9PSAwKSkKICAgIAogICAgIyA1LiBDb21tdW5pdHkgZGV0ZWN0aW9uOiBXYWxrdHJhcCBhbGdvcml0aG0KICAgIHdhbGt0cmFwX2NvbW11bml0aWVzIDwtIHdhbGt0cmFwLmNvbW11bml0eShnLnRyYWRlLCB3ZWlnaHRzID0gRShnLnRyYWRlKSR3ZWlnaHQpCiAgICAjIEdldCB0aGUgY29tbXVuaXR5IG1lbWJlcnNoaXAgdmVjdG9yCiAgICBtZW1iZXJzaGlwX3ZlYyA8LSBtZW1iZXJzaGlwKHdhbGt0cmFwX2NvbW11bml0aWVzKQogICAgIyBBZGQgdGhlIGNvbW11bml0eSBhcyBhbiBhdHRyaWJ1dGUgdG8gdGhlIHZlY3RvcnMKICAgIFYoZy50cmFkZSkkY29tbXVuaXR5IDwtIG1lbWJlcnNoaXBfdmVjW1YoZy50cmFkZSkkbmFtZV0KICAgIAogICAgIyBDcmVhdGUgYSBuZXcgZ3JhcGggb2JqZWN0IHdpdGggY29tbXVuaXR5IG1lbWJlcnNoaXBzIGFzIHZlcnRleCBhdHRyaWJ1dGVzCiAgICBnX2NvbW0gPC0gc2V0X3ZlcnRleF9hdHRyKGcudHJhZGUsICJjb21tdW5pdHkiLCB2YWx1ZSA9IHdhbGt0cmFwX2NvbW11bml0aWVzJG1lbWJlcnNoaXApCiAgICAjIENvbnZlcnQgdG8gaWdyYXBoIG9iamVjdAogICAgZ19jb21tIDwtIGdyYXBoX2Zyb21fZGF0YV9mcmFtZShnZXQuZWRnZWxpc3QoZ19jb21tKSwgZGlyZWN0ZWQgPSBUUlVFKQogICAgIyBDb21wdXRlIGVkZ2UgYmV0d2Vlbm5lc3MgZm9yIHRoZSBlbnRpcmUgZ3JhcGgKICAgIGViIDwtIGlncmFwaDo6ZWRnZS5iZXR3ZWVubmVzcyhnX2NvbW0pCiAgICAjIEZpbmQgdGhlIHRvcCAxMCBlZGdlcyB3aXRoIHRoZSBoaWdoZXN0IGVkZ2UgYmV0d2Vlbm5lc3MKICAgIHRvcDEwX2ViIDwtIG9yZGVyKGViLCBkZWNyZWFzaW5nID0gVFJVRSlbMToxMF0KICAgICMgR2VuZXJhdGUgYSB2ZWN0b3Igb2YgY29sb3JzIHVzaW5nIHRoZSByYWluYm93KCkgZnVuY3Rpb24KICAgIGNvbG9yX2ViIDwtIHJhaW5ib3cobGVuZ3RoKEUoZ19jb21tKSkpCiAgICAjIFNldCB0aGUgY29sb3Igb2YgdGhlIHRvcCAxMCBlZGdlcyB3aXRoIGhpZ2hlc3QgZWRnZSBiZXR3ZWVubmVzcyB0byByZWQKICAgIGNvbG9yX2ViW3RvcDEwX2ViXSA8LSAiIzAwMDAwMCIKICAgICMgU2V0IHRoZSBjb2xvciBvZiB0aGUgcmVtYWluaW5nIGVkZ2VzIHRvIHRyYW5zcGFyZW50CiAgICBjb2xvcl9lYlstdG9wMTBfZWJdIDwtICJ0cmFuc3BhcmVudCIKICAgIAogICAgIyBleHRyYWN0IHRoZSB2ZXJ0aWNlcyBhbmQgZWRnZXMgZnJvbSB0aGUgZyBncmFwaCBvYmplY3QgYW5kIHN0b3JlIHRoZW0gYXMgYSBkYXRhIGZyYW1lCiAgICBnZyA8LSBnZXQuZGF0YS5mcmFtZShnLnRyYWRlLCAiYm90aCIpCiAgICAjIGFzc2lnbiB2ZXJ0IHZhcmlhYmxlIHdpdGggZGF0YWZyYW1lIGluZm8gb24gdGhlIHZlcnRpY2VzICsgYWRkIHRoZSBzcGF0aWFsIGNvb3JkaW5hdGVzIG9mIHRoZSBwb2ludHMgdG8gdmVydAogICAgdmVydCA8LSBnZyR2ZXJ0aWNlcwogICAgY29vcmRpbmF0ZXModmVydCkgPC0gfmxvbiArIGxhdAogICAgCiAgICAjIGFzc2lnbiBlZGdlcyBhbmQgd2VpZ2h0cyB2YXJpYWJsZQogICAgZWRnZXMgPC0gZ2ckZWRnZXMKICAgIHdlaWdodHMgPC0gRShnLnRyYWRlKSR3ZWlnaHQKICAgIAogICAgIyBDcmVhdGUgYSBsaXN0IG9mIHNwYXRpYWwgb2JqZWN0cwogICAgZWRnZXMgPC0gbGFwcGx5KDE6bnJvdyhlZGdlcyksIGZ1bmN0aW9uKGkpIAogICAgICB7YXMocmJpbmQodmVydFt2ZXJ0JG5hbWUgPT0gZWRnZXNbaSwgImZyb20iXSwgXSwgCiAgICAgICAgICAgICAgICB2ZXJ0W3ZlcnQkbmFtZSA9PSBlZGdlc1tpLCAidG8iXSwgXSksIAogICAgICAgICAgIlNwYXRpYWxMaW5lcyIpCiAgICAgIH0pCiAgICAKICAgICMgYXNzaWduIHVuaXF1ZSBJRHMgdG8gZWFjaCBvZiB0aGUgU3BhdGlhbExpbmVzIG9iamVjdHMKICAgIGZvciAoaSBpbiBzZXFfYWxvbmcoZWRnZXMpKSB7CiAgICAgIGVkZ2VzW1tpXV0gPC0gc3BDaEZJRHMoZWRnZXNbW2ldXSwgYXMuY2hhcmFjdGVyKGkpKQogICAgfQogICAgCiAgICAjIGNvbWJpbmUgYWxsIG9mIHRoZSBTcGF0aWFsTGluZXMgb2JqZWN0cyArIG5ldyBTcGF0aWFsTGluZXNEYXRhRnJhbWUgb2JqZWN0IHVzaW5nIHRoZSBjb21iaW5lZCBTcGF0aWFsTGluZXMgb2JqZWN0IGFuZCB0aGUgZWRnZXNfZGYgZGF0YSBmcmFtZS4KICAgIGVkZ2VzIDwtIGRvLmNhbGwocmJpbmQsIGVkZ2VzKQogICAgZWRnZXNfZGYgPC0gZGF0YS5mcmFtZShpZCA9IHNlcV9hbG9uZyhlZGdlcyksIHdlaWdodCA9IHdlaWdodHMpCiAgICBlZGdlcyA8LSBTcGF0aWFsTGluZXNEYXRhRnJhbWUoZWRnZXMsIGRhdGEgPSBlZGdlc19kZikKICAgIAogICAgIyBwbG90IG1hcAogICAgY29tbXVuaXR5X2NvbG9ycyA8LSBodWVfcGFsKCkobGVuZ3RoKHVuaXF1ZSh2ZXJ0JGNvbW11bml0eSkpKQogICAgCiAgICBsZWFmbGV0KHZlcnQpICU+JSAKICAgICAgYWRkUHJvdmlkZXJUaWxlcygiU3RhbWVuLlRvbmVyIiwgb3B0aW9ucyA9IAogICAgICAgICAgICAgICAgICAgICAgICAgcHJvdmlkZXJUaWxlT3B0aW9ucyhiYWNrZ3JvdW5kQ29sb3IgPSAiI2YyZjJmMiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG9wYWNpdHkgPSAwLjQpKSAlPiUgCiAgICAgIGFkZENpcmNsZU1hcmtlcnMoZGF0YSA9IHZlcnQsIHJhZGl1cyA9IDQsIAogICAgICAgICAgICAgICAgICAgY29sb3IgPSBjb21tdW5pdHlfY29sb3JzW3ZlcnQkY29tbXVuaXR5XSwgCiAgICAgICAgICAgICAgICAgICBmaWxsQ29sb3IgPSBjb21tdW5pdHlfY29sb3JzW3ZlcnQkY29tbXVuaXR5XSwgCiAgICAgICAgICAgICAgICAgICBmaWxsT3BhY2l0eSA9IDAuOCwKICAgICAgICAgICAgICAgICAgIHN0cm9rZSA9IEZBTFNFKSAlPiUgCiAgICAgIGFkZFBvbHlsaW5lcyhkYXRhID0gZWRnZXMsIHdlaWdodCA9IDIsIGNvbG9yID0gfmNvbG9yX2ViKQogIH0pCiAgCiAgb3V0cHV0JGNsdXN0ZXIgPC0gcmVuZGVyUGxvdCh7CiAgICAjIDEuIE1ha2UgZWRnZSBsaXN0CiAgICBkdC50cmFkZS55ZWFyIDwtIGR0LnRyYWRlW2R0LnRyYWRlJHllYXIgPT0gaW5wdXQkeWVhciwgXQogICAgZHQudHJhZGUueWVhciA8LSBkdC50cmFkZS55ZWFyW2R0LnRyYWRlLnllYXIkdHJhZGVfdmFsdWVfdXNkID4gaW5wdXQkd2VpZ2h0LCBdCiAgICBkdC50cmFkZS5lZGdlbGlzdCA8LSBkdC50cmFkZS55ZWFyWyAsIGMoJ3JlcG9ydGVyX25hbWUnLCAncGFydG5lcl9uYW1lJyldCiAgICAKICAgICMgMi4gQ29udmVydCBlZGdlIGxpc3QgdG8gYW4gaWdyYXBoIG5ldHdvcmsKICAgICMgaWdyYXBoIHdhbnRzIG91ciBkYXRhIGluIG1hdHJpeCBmb3JtYXQKICAgIG0udHJhZGUgPC0gYXMubWF0cml4KGR0LnRyYWRlLmVkZ2VsaXN0KSAKICAgIGcudHJhZGUgPC0gZ3JhcGhfZnJvbV9lZGdlbGlzdChtLnRyYWRlLCBkaXJlY3RlZD1UUlVFKQogICAgCiAgICAjIDMuIFNldCB2ZXJ0ZXggYXR0cmlidXRlcyB1c2luZyBzZXRfdmVydGV4X2F0dHIoKQogICAgbC5jb3VudHJpZXMgPC0gYXMubGlzdCh1bmlxdWUoZHQudHJhZGUueWVhciRyZXBvcnRlcl9uYW1lKSkKICAgIGR0LmNvdW50cnkuY29vcmRpbmF0ZXMgPC0gZHQudHJhZGUueWVhciAlPiUgZGlzdGluY3QocGFydG5lcl9uYW1lLCAua2VlcF9hbGwgPSBUUlVFKQogICAgZHQuY291bnRyeS5jb29yZGluYXRlcyA8LSBkdC5jb3VudHJ5LmNvb3JkaW5hdGVzWywgYygicGFydG5lcl9uYW1lIiwgInBhcnRuZXJfbGF0IiwgInBhcnRuZXJfbG9uZyIpXQogICAgbWV0YSA8LSBkdC5jb3VudHJ5LmNvb3JkaW5hdGVzICU+JSByZW5hbWUoIm5hbWUiID0gInBhcnRuZXJfbmFtZSIsICJsYXQiID0gInBhcnRuZXJfbGF0IiwgImxvbiIgPSAicGFydG5lcl9sb25nIikKCiAgICB2ZXJ0ZXhfYXR0cnMgPC0gYXMubGlzdChtZXRhWywgLTFdKSAjIGV4Y2x1ZGUgdGhlICduYW1lJyBjb2x1bW4KICAgIFYoZy50cmFkZSkkbGF0IDwtIHZlcnRleF9hdHRycyRsYXQKICAgIFYoZy50cmFkZSkkbG9uIDwtIHZlcnRleF9hdHRycyRsb24KICAgIAogICAgIyA0LiBTZXQgdGhlIHdlaWdodCBvZiB0aGUgZWRnZXMgKHRyYWRlX3ZhbHVlX3VzZCkgJiBhZGQgdGhlIHllYXIgYXMgYW4gYXR0cmlidXRlCiAgICBFKGcudHJhZGUpJHdlaWdodCA8LSBkdC50cmFkZS55ZWFyJHRyYWRlX3ZhbHVlX3VzZAogICAgIyBSZW1vdmUgdmVydGljZXMgd2l0aCBkZWdyZWUgMAogICAgZy50cmFkZSA8LSBkZWxldGUudmVydGljZXMoZy50cmFkZSwgd2hpY2goZGVncmVlKGcudHJhZGUpID09IDApKQogICAgCiAgICAjIDUuIENvbW11bml0eSBkZXRlY3Rpb24gYWxnb3JpdGhtcwogICAgIyBSdW4gV2Fsa3RyYXAgYWxnb3JpdGhtCiAgICB3YWxrdHJhcF9jb21tdW5pdGllcyA8LSB3YWxrdHJhcC5jb21tdW5pdHkoZy50cmFkZSwgd2VpZ2h0cyA9IEUoZy50cmFkZSkkd2VpZ2h0KQogICAgCiAgICAjIENyZWF0ZSBhIG5ldyBncmFwaCBvYmplY3Qgd2l0aCBjb21tdW5pdHkgbWVtYmVyc2hpcHMgYXMgdmVydGV4IGF0dHJpYnV0ZXMKICAgIGdfY29tbSA8LSBzZXRfdmVydGV4X2F0dHIoZy50cmFkZSwgImNvbW11bml0eSIsIHZhbHVlID0gd2Fsa3RyYXBfY29tbXVuaXRpZXMkbWVtYmVyc2hpcCkKICAgICMgQ29udmVydCB0byBpZ3JhcGggb2JqZWN0CiAgICBnX2NvbW0gPC0gZ3JhcGhfZnJvbV9kYXRhX2ZyYW1lKGdldC5lZGdlbGlzdChnX2NvbW0pLCBkaXJlY3RlZCA9IFRSVUUpCiAgICAjIENvbXB1dGUgZWRnZSBiZXR3ZWVubmVzcyBmb3IgdGhlIGVudGlyZSBncmFwaAogICAgZWIgPC0gaWdyYXBoOjplZGdlLmJldHdlZW5uZXNzKGdfY29tbSkKICAgICMgRmluZCB0aGUgdG9wIDEwIGVkZ2VzIHdpdGggdGhlIGhpZ2hlc3QgZWRnZSBiZXR3ZWVubmVzcwogICAgdG9wMTBfZWIgPC0gb3JkZXIoZWIsIGRlY3JlYXNpbmcgPSBUUlVFKVsxOjEwXQogICAgIyBHZW5lcmF0ZSBhIHZlY3RvciBvZiBjb2xvcnMgdXNpbmcgdGhlIHJhaW5ib3coKSBmdW5jdGlvbgogICAgY29sb3JfZWIgPC0gcmFpbmJvdyhsZW5ndGgoRShnX2NvbW0pKSkKICAgICMgU2V0IHRoZSBjb2xvciBvZiB0aGUgdG9wIDEwIGVkZ2VzIHdpdGggaGlnaGVzdCBlZGdlIGJldHdlZW5uZXNzIHRvIHJlZAogICAgY29sb3JfZWJbdG9wMTBfZWJdIDwtICIjMDAwMDAwIgogICAgIyBTZXQgdGhlIGNvbG9yIG9mIHRoZSByZW1haW5pbmcgZWRnZXMgdG8gdHJhbnNwYXJlbnQKICAgIGNvbG9yX2ViWy10b3AxMF9lYl0gPC0gImdyZXkiCiAgICAKICAgIHBsb3Qod2Fsa3RyYXBfY29tbXVuaXRpZXMsIGcudHJhZGUsIHZlcnRleC5zaXplID0gNSwgdmVydGV4LmxhYmVsLmNleCA9IDAuMywgZWRnZS5hcnJvdy5zaXplPTAuMywgdmVydGV4LmNvbG9yID0gd2Fsa3RyYXBfY29tbXVuaXRpZXMkbWVtYmVyc2hpcCwgZWRnZS5jb2xvciA9IGNvbG9yX2ViKQogIH0pCiAgCiAgb3V0cHV0JHRleHRrcGkgPC0gcmVuZGVyVUkoewogICAgIyAxLiBNYWtlIGVkZ2UgbGlzdAogICAgZHQudHJhZGUueWVhciA8LSBkdC50cmFkZVtkdC50cmFkZSR5ZWFyID09IGlucHV0JHllYXIsIF0KICAgIGR0LnRyYWRlLnllYXIgPC0gZHQudHJhZGUueWVhcltkdC50cmFkZS55ZWFyJHRyYWRlX3ZhbHVlX3VzZCA+IGlucHV0JHdlaWdodCwgXQogICAgZHQudHJhZGUuZWRnZWxpc3QgPC0gZHQudHJhZGUueWVhclsgLCBjKCdyZXBvcnRlcl9uYW1lJywgJ3BhcnRuZXJfbmFtZScpXQogICAgCiAgICAjIDIuIENvbnZlcnQgZWRnZSBsaXN0IHRvIGFuIGlncmFwaCBuZXR3b3JrCiAgICAjIGlncmFwaCB3YW50cyBvdXIgZGF0YSBpbiBtYXRyaXggZm9ybWF0CiAgICBtLnRyYWRlIDwtIGFzLm1hdHJpeChkdC50cmFkZS5lZGdlbGlzdCkgCiAgICBnLnRyYWRlIDwtIGdyYXBoX2Zyb21fZWRnZWxpc3QobS50cmFkZSwgZGlyZWN0ZWQ9VFJVRSkKICAgIAogICAgIyAzLiBTZXQgdmVydGV4IGF0dHJpYnV0ZXMgdXNpbmcgc2V0X3ZlcnRleF9hdHRyKCkKICAgIGwuY291bnRyaWVzIDwtIGFzLmxpc3QodW5pcXVlKGR0LnRyYWRlLnllYXIkcmVwb3J0ZXJfbmFtZSkpCiAgICBkdC5jb3VudHJ5LmNvb3JkaW5hdGVzIDwtIGR0LnRyYWRlLnllYXIgJT4lIGRpc3RpbmN0KHBhcnRuZXJfbmFtZSwgLmtlZXBfYWxsID0gVFJVRSkKICAgIGR0LmNvdW50cnkuY29vcmRpbmF0ZXMgPC0gZHQuY291bnRyeS5jb29yZGluYXRlc1ssIGMoInBhcnRuZXJfbmFtZSIsICJwYXJ0bmVyX2xhdCIsICJwYXJ0bmVyX2xvbmciKV0KICAgIG1ldGEgPC0gZHQuY291bnRyeS5jb29yZGluYXRlcyAlPiUgcmVuYW1lKCJuYW1lIiA9ICJwYXJ0bmVyX25hbWUiLCAibGF0IiA9ICJwYXJ0bmVyX2xhdCIsICJsb24iID0gInBhcnRuZXJfbG9uZyIpCiAgICAKICAgIHZlcnRleF9hdHRycyA8LSBhcy5saXN0KG1ldGFbLCAtMV0pICMgZXhjbHVkZSB0aGUgJ25hbWUnIGNvbHVtbgogICAgVihnLnRyYWRlKSRsYXQgPC0gdmVydGV4X2F0dHJzJGxhdAogICAgVihnLnRyYWRlKSRsb24gPC0gdmVydGV4X2F0dHJzJGxvbgogICAgCiAgICAjIDQuIFNldCB0aGUgd2VpZ2h0IG9mIHRoZSBlZGdlcyAodHJhZGVfdmFsdWVfdXNkKSAmIGFkZCB0aGUgeWVhciBhcyBhbiBhdHRyaWJ1dGUKICAgIEUoZy50cmFkZSkkd2VpZ2h0IDwtIGR0LnRyYWRlLnllYXIkdHJhZGVfdmFsdWVfdXNkCiAgICAjIFJlbW92ZSB2ZXJ0aWNlcyB3aXRoIGRlZ3JlZSAwCiAgICBnLnRyYWRlIDwtIGRlbGV0ZS52ZXJ0aWNlcyhnLnRyYWRlLCB3aGljaChkZWdyZWUoZy50cmFkZSkgPT0gMCkpCiAgICAKICAgICMgNS4gQ29tbXVuaXR5IGRldGVjdGlvbjogV2Fsa3RyYXAgYWxnb3JpdGhtCiAgICB3YWxrdHJhcF9jb21tdW5pdGllcyA8LSB3YWxrdHJhcC5jb21tdW5pdHkoZy50cmFkZSwgd2VpZ2h0cyA9IEUoZy50cmFkZSkkd2VpZ2h0KQogICAgCiAgICAjIGdldCB0aGUgYXZlcmFnZSBwYXRoIGxlbmd0aCBmb3IgZWFjaCBjb21tdW5pdHkKICAgIGFwbF9jb21tIDwtIGxhcHBseSh1bmlxdWUod2Fsa3RyYXBfY29tbXVuaXRpZXMkbWVtYmVyc2hpcCksIGZ1bmN0aW9uKG0pIGF2ZXJhZ2UucGF0aC5sZW5ndGgoaW5kdWNlZF9zdWJncmFwaChnLnRyYWRlLCBWKGcudHJhZGUpW3dhbGt0cmFwX2NvbW11bml0aWVzJG1lbWJlcnNoaXAgPT0gbV0pKSkKICAgIGFwbF9hbGwgPC0gYXZlcmFnZS5wYXRoLmxlbmd0aChnLnRyYWRlKQogICAgCiAgICBIVE1MKHBhc3RlKCIgIiwgIjxicj4iLAogICAgICAgICAgICAgICAiICIsICI8YnI+IiwKICAgICAgICAgICAgICAgIk51bWJlciBvZiBjb21tdW5pdGllcyBkZXRlY3RlZCBieSBXYWxrdHJhcCBhbGdvcml0aG06ICIsIGxlbmd0aCh1bmlxdWUod2Fsa3RyYXBfY29tbXVuaXRpZXMkbWVtYmVyc2hpcCkpLCAiPGJyPiIsCiAgICAgICAgICAgICAgICIgIiwgIjxicj4iLAogICAgICAgICAgICAgICAiTW9kdWxhcml0eSBzY29yZSBvZiBXYWxrdHJhcCBhbGdvcml0aG06ICIsIG1vZHVsYXJpdHkod2Fsa3RyYXBfY29tbXVuaXRpZXMsIGcpLCAiPGJyPiIsCiAgICAgICAgICAgICAgICIgIiwgIjxicj4iLAogICAgICAgICAgICAgICAiU2l6ZXMgb2YgdGhlIGNvbW11bml0aWVzOiAiLCBwYXN0ZSh1bm5hbWUoc2l6ZXMod2Fsa3RyYXBfY29tbXVuaXRpZXMpKSwgY29sbGFwc2UgPSAiLCAiKSwgIjxicj4iLAogICAgICAgICAgICAgICAiICIsICI8YnI+IiwKICAgICAgICAgICAgICAgIiAiLCAiPGJyPiIsCiAgICAgICAgICAgICAgICJBdmVyYWdlIHBhdGggbGVuZ3RoIG9mIHRoZSB0b3RhbCB0cmFkZSBuZXR3b3JrOiAiLCBhcGxfYWxsLCAiPGJyPiIsCiAgICAgICAgICAgICAgICIgIiwgIjxicj4iLAogICAgICAgICAgICAgICAiQXZlcmFnZSBwYXRoIGxlbmd0aCBvZiB0aGUgY29tbXVuaXRpZXM6ICIsIHBhc3RlKHVubmFtZShhcGxfY29tbSksIGNvbGxhcHNlID0gIiwgIiksICI8YnI+IikpCiAgfSkKICAKICBvdXRwdXQkdGV4dHN0YXQgPC0gcmVuZGVyVUkoewogICAgSFRNTChwYXN0ZSgiICIsICI8YnI+IiwKICAgICAgICAgICAgICAgIiAiLCAiPGJyPiIsCiAgICAgICAgICAgICAgICIgbGFsYWxhIEkgbmVlZCB0byB3cml0ZSB0ZXh0IGhlcmUiLCAiPGJyPiIpKQogIH0pCn0KCiMgQ3JlYXRlIGEgU2hpbnkgYXBwIG9iamVjdCAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgpzaGlueUFwcCh1aSA9IHVpLCBzZXJ2ZXIgPSBzZXJ2ZXIpCmBgYAoKYGBge3J9CiMjIFRSWSBUTyBQTE9UIEEgTUFQCmxpYnJhcnkoZGF0YS50YWJsZSkKbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KGlncmFwaCkKbGlicmFyeShzaGlueSkKbGlicmFyeShEVCkgICAgICAgICAgICAgCmxpYnJhcnkoc2hpbnkpICAgICAgICAgCmxpYnJhcnkoZ2dtYXApCmxpYnJhcnkobGVhZmxldCkKbGlicmFyeShic2xpYikKbGlicmFyeShzZikKbGlicmFyeShnZW9zcGhlcmUpCmxpYnJhcnkoUkNvbG9yQnJld2VyKQpsaWJyYXJ5KHNwKQpsaWJyYXJ5KGRwbHlyKSAKCgojIExvYWQgZGF0YSBmcm9tIGZpbGUgY29tcHRhYl8yMDE4LTAxLTI5IDE2XzAwX2NvbW1hX3NlcGFyYXRlZC5jc3YgCmR0LnRyYWRlIDwtIGZyZWFkKCIuLi8uLi9kYXRhL2NsZWFuZWRfdHJhZGVfZGF0YS5jc3YiKQoKICAgICMgMS4gTWFrZSBlZGdlIGxpc3QKICAgIGR0LnRyYWRlLnllYXIgPC0gZHQudHJhZGVbZHQudHJhZGUkeWVhciA9PSAyMDIxLCBdCiAgICBkdC50cmFkZS55ZWFyIDwtIGR0LnRyYWRlLnllYXJbZHQudHJhZGUueWVhciR0cmFkZV92YWx1ZV91c2QgPiAwLCBdCiAgICBkdC50cmFkZS5lZGdlbGlzdCA8LSBkdC50cmFkZS55ZWFyWyAsIGMoJ3JlcG9ydGVyX25hbWUnLCAncGFydG5lcl9uYW1lJyldCiAgICAKICAgICMgMi4gQ29udmVydCBlZGdlIGxpc3QgdG8gYW4gaWdyYXBoIG5ldHdvcmsKICAgICMgaWdyYXBoIHdhbnRzIG91ciBkYXRhIGluIG1hdHJpeCBmb3JtYXQKICAgIG0udHJhZGUgPC0gYXMubWF0cml4KGR0LnRyYWRlLmVkZ2VsaXN0KSAKICAgIGcudHJhZGUgPC0gZ3JhcGhfZnJvbV9lZGdlbGlzdChtLnRyYWRlLCBkaXJlY3RlZD1UUlVFKQogICAgCiAgICAjIDMuIFNldCB2ZXJ0ZXggYXR0cmlidXRlcyB1c2luZyBzZXRfdmVydGV4X2F0dHIoKQogICAgbC5yZXBvcnRlcnMgPC0gYXMubGlzdCh1bmlxdWUoZHQudHJhZGUueWVhciRyZXBvcnRlcl9uYW1lKSkKICAgIGwucGFydG5lcnMgPC0gYXMubGlzdCh1bmlxdWUoZHQudHJhZGUueWVhciRwYXJ0bmVyX25hbWUpKQogICAgbC5jb3VudHJpZXMgPC0gYXMubGlzdCh1bmlxdWUoYXBwZW5kKGwucmVwb3J0ZXJzLCBsLnBhcnRuZXJzKSkpCiAgICBkdC5jb3VudHJ5LmNvb3JkaW5hdGVzIDwtIGR0LnRyYWRlLnllYXIgJT4lIGRpc3RpbmN0KHBhcnRuZXJfbmFtZSwgLmtlZXBfYWxsID0gVFJVRSkKICAgIGR0LmNvdW50cnkuY29vcmRpbmF0ZXMgPC0gZHQuY291bnRyeS5jb29yZGluYXRlc1ssIGMoInBhcnRuZXJfbmFtZSIsICJwYXJ0bmVyX2xhdCIsICJwYXJ0bmVyX2xvbmciKV0KICAgIG1ldGEgPC0gZHQuY291bnRyeS5jb29yZGluYXRlcyAlPiUgcmVuYW1lKCJuYW1lIiA9ICJwYXJ0bmVyX25hbWUiLCAibGF0IiA9ICJwYXJ0bmVyX2xhdCIsICJsb24iID0gInBhcnRuZXJfbG9uZyIpCgogICAgbWV0YSA8LSBtZXRhW21hdGNoKFYoZy50cmFkZSkkbmFtZSwgbWV0YSRuYW1lKSwgXQogICAgVihnLnRyYWRlKSRsYXQgPC0gbWV0YSRsYXQKICAgIFYoZy50cmFkZSkkbG9uIDwtIG1ldGEkbG9uCiAgICAKICAgICMgNC4gU2V0IHRoZSB3ZWlnaHQgb2YgdGhlIGVkZ2VzICh0cmFkZV92YWx1ZV91c2QpICYgYWRkIHRoZSB5ZWFyIGFzIGFuIGF0dHJpYnV0ZQogICAgRShnLnRyYWRlKSR3ZWlnaHQgPC0gZHQudHJhZGUueWVhciR0cmFkZV92YWx1ZV91c2QKICAgICMgUmVtb3ZlIHZlcnRpY2VzIHdpdGggZGVncmVlIDAKICAgIGcudHJhZGUgPC0gZGVsZXRlLnZlcnRpY2VzKGcudHJhZGUsIHdoaWNoKGRlZ3JlZShnLnRyYWRlKSA9PSAwKSkKICAgIAogICAgIyA1LiBDb21tdW5pdHkgZGV0ZWN0aW9uIGFsZ29yaXRobXMKICAgICMgUnVuIFdhbGt0cmFwIGFsZ29yaXRobQogICAgd2Fsa3RyYXBfY29tbXVuaXRpZXMgPC0gd2Fsa3RyYXAuY29tbXVuaXR5KGcudHJhZGUsIHdlaWdodHMgPSBFKGcudHJhZGUpJHdlaWdodCkKICAgICMgR2V0IHRoZSBjb21tdW5pdHkgbWVtYmVyc2hpcCB2ZWN0b3IKICAgIG1lbWJlcnNoaXBfdmVjIDwtIG1lbWJlcnNoaXAod2Fsa3RyYXBfY29tbXVuaXRpZXMpCiAgICAjIEFkZCB0aGUgY29tbXVuaXR5IGFzIGFuIGF0dHJpYnV0ZSB0byB0aGUgdmVjdG9ycwogICAgVihnLnRyYWRlKSRjb21tdW5pdHkgPC0gbWVtYmVyc2hpcF92ZWNbVihnLnRyYWRlKSRuYW1lXQogICAgCiAgICAjIENyZWF0ZSBhIG5ldyBncmFwaCBvYmplY3Qgd2l0aCBjb21tdW5pdHkgbWVtYmVyc2hpcHMgYXMgdmVydGV4IGF0dHJpYnV0ZXMKICAgIGdfY29tbSA8LSBzZXRfdmVydGV4X2F0dHIoZy50cmFkZSwgImNvbW11bml0eSIsIHZhbHVlID0gd2Fsa3RyYXBfY29tbXVuaXRpZXMkbWVtYmVyc2hpcCkKICAgICMgQ29udmVydCB0byBpZ3JhcGggb2JqZWN0CiAgICBnX2NvbW0gPC0gZ3JhcGhfZnJvbV9kYXRhX2ZyYW1lKGdldC5lZGdlbGlzdChnX2NvbW0pLCBkaXJlY3RlZCA9IFRSVUUpCiAgICAjIENvbXB1dGUgZWRnZSBiZXR3ZWVubmVzcyBmb3IgdGhlIGVudGlyZSBncmFwaAogICAgZWIgPC0gaWdyYXBoOjplZGdlLmJldHdlZW5uZXNzKGdfY29tbSkKICAgICMgRmluZCB0aGUgdG9wIDEwIGVkZ2VzIHdpdGggdGhlIGhpZ2hlc3QgZWRnZSBiZXR3ZWVubmVzcwogICAgdG9wMTBfZWIgPC0gb3JkZXIoZWIsIGRlY3JlYXNpbmcgPSBUUlVFKVsxOjEwXQogICAgIyBHZW5lcmF0ZSBhIHZlY3RvciBvZiBjb2xvcnMgdXNpbmcgdGhlIHJhaW5ib3coKSBmdW5jdGlvbgogICAgY29sb3JfZWIgPC0gcmFpbmJvdyhsZW5ndGgoRShnX2NvbW0pKSkKICAgICMgU2V0IHRoZSBjb2xvciBvZiB0aGUgdG9wIDEwIGVkZ2VzIHdpdGggaGlnaGVzdCBlZGdlIGJldHdlZW5uZXNzIHRvIHJlZAogICAgY29sb3JfZWJbdG9wMTBfZWJdIDwtICJyZWQiCiAgICAjIFNldCB0aGUgY29sb3Igb2YgdGhlIHJlbWFpbmluZyBlZGdlcyB0byB0cmFuc3BhcmVudAogICAgY29sb3JfZWJbLXRvcDEwX2ViXSA8LSAidHJhbnNwYXJlbnQiCiAgICAKICAgICMgZXh0cmFjdCB0aGUgdmVydGljZXMgYW5kIGVkZ2VzIGZyb20gdGhlIGcgZ3JhcGggb2JqZWN0IGFuZCBzdG9yZSB0aGVtIGFzIGEgZGF0YSBmcmFtZQogICAgZ2cgPC0gZ2V0LmRhdGEuZnJhbWUoZy50cmFkZSwgImJvdGgiKQogICAgIyBhc3NpZ24gdmVydCB2YXJpYWJsZSB3aXRoIGRhdGFmcmFtZSBpbmZvIG9uIHRoZSB2ZXJ0aWNlcyArIGFkZCB0aGUgc3BhdGlhbCBjb29yZGluYXRlcyBvZiB0aGUgcG9pbnRzIHRvIHZlcnQKICAgIHZlcnQgPC0gZ2ckdmVydGljZXMKICAgIGNvb3JkaW5hdGVzKHZlcnQpIDwtIH5sb24gKyBsYXQKICAgICMgYXNzaWduIGVkZ2VzIGFuZCB3ZWlnaHRzIHZhcmlhYmxlCiAgICBlZGdlcyA8LSBnZyRlZGdlcwogICAgd2VpZ2h0cyA8LSBFKGcudHJhZGUpJHdlaWdodAogICAgCiAgICAjIENyZWF0ZSBhIGxpc3Qgb2Ygc3BhdGlhbCBvYmplY3RzCiAgICBlZGdlcyA8LSBsYXBwbHkoMTpucm93KGVkZ2VzKSwgZnVuY3Rpb24oaSkgCiAgICAgIHthcyhyYmluZCh2ZXJ0W3ZlcnQkbmFtZSA9PSBlZGdlc1tpLCAiZnJvbSJdLCBdLCAKICAgICAgICAgICAgICAgIHZlcnRbdmVydCRuYW1lID09IGVkZ2VzW2ksICJ0byJdLCBdKSwgCiAgICAgICAgICAiU3BhdGlhbExpbmVzIikKICAgICAgfSkKICAgICMgYXNzaWduIHVuaXF1ZSBJRHMgdG8gZWFjaCBvZiB0aGUgU3BhdGlhbExpbmVzIG9iamVjdHMKICAgIGZvciAoaSBpbiBzZXFfYWxvbmcoZWRnZXMpKSB7CiAgICAgIGVkZ2VzW1tpXV0gPC0gc3BDaEZJRHMoZWRnZXNbW2ldXSwgYXMuY2hhcmFjdGVyKGkpKQogICAgfQogICAgIyBjb21iaW5lIGFsbCBvZiB0aGUgU3BhdGlhbExpbmVzIG9iamVjdHMgKyBuZXcgU3BhdGlhbExpbmVzRGF0YUZyYW1lIG9iamVjdCB1c2luZyB0aGUgY29tYmluZWQgU3BhdGlhbExpbmVzIG9iamVjdCBhbmQgdGhlIGVkZ2VzX2RmIGRhdGEgZnJhbWUuCiAgICBlZGdlcyA8LSBkby5jYWxsKHJiaW5kLCBlZGdlcykKICAgIGVkZ2VzX2RmIDwtIGRhdGEuZnJhbWUoaWQgPSBzZXFfYWxvbmcoZWRnZXMpLCB3ZWlnaHQgPSB3ZWlnaHRzKQogICAgZWRnZXMgPC0gU3BhdGlhbExpbmVzRGF0YUZyYW1lKGVkZ2VzLCBkYXRhID0gZWRnZXNfZGYpCiAgICAKICAgICMgcGxvdCBtYXAKICAgIGNvbW11bml0eV9jb2xvcnMgPC0gYygiYmx1ZSIsICJncmVlbiIsICJyZWQiKQogICAgCiAgICBsZWFmbGV0KHZlcnQpICU+JSAKICAgICAgYWRkVGlsZXMoKSAlPiUgCiAgICAgIGFkZENpcmNsZU1hcmtlcnMoZGF0YSA9IHZlcnQsIHJhZGl1cyA9IDIsIAogICAgICAgICAgICAgICAgICAgY29sb3IgPSBjb21tdW5pdHlfY29sb3JzW3ZlcnQkY29tbXVuaXR5XSwgCiAgICAgICAgICAgICAgICAgICBmaWxsQ29sb3IgPSBjb21tdW5pdHlfY29sb3JzW3ZlcnQkY29tbXVuaXR5XSwgCiAgICAgICAgICAgICAgICAgICBmaWxsT3BhY2l0eSA9IDAuOCwKICAgICAgICAgICAgICAgICAgIHN0cm9rZSA9IEZBTFNFKSAlPiUgCiAgICAgIGFkZFBvbHlsaW5lcyhkYXRhID0gZWRnZXMsIHdlaWdodCA9IDIsIGNvbG9yID0gfmNvbG9yX2ViKQoKYGBgCgoKCgo=